/*
 * explorer.c
 *
 *  Created on: Dec 23, 2010
 *      Author: cesar
 */

#include "explorer.h"

p_st_folder exploreFolder(char *dir_uri, p_ent ent){
	// Variaveis locais
	DIR *dir, *dir_type;
	p_st_folder folderView = NULL;
	int num_blocks;
	char *new_dir_uri;

	DebugPrint("Percorrendo o diretorio %s", dir_uri);

	//Inicializa a estrutura inicial
	folderView = initFolderSt(clone_ent(ent));

	dir = opendir (dir_uri);

	if (dir != NULL){
		//Percorre os arquivos do diretorio
		while ((ent = readdir (dir)) != NULL) {
			if( strcmp( ent->d_name, "." ) == 0 ||
					strcmp( ent->d_name, ".." ) == 0 ) {
				continue;
			}

			//Verifica se o arquivo eh uma pasta
			// A verificacao nao esta sendo feita pelo ent->d_type pela portabilidade
			new_dir_uri = malloc(strlen(dir_uri)+strlen(ent->d_name)+2);	// \0+/
			sprintf(new_dir_uri, "%s/%s", dir_uri, ent->d_name);
			dir_type = opendir (new_dir_uri);

			if(dir_type != NULL){
				closedir (dir_type);
				// Aloca mais memoria caso necessario
				if((folderView->num_folders % MALLOC_EST_FOLDER) == 0){
					num_blocks = (folderView->num_folders / MALLOC_EST_FOLDER) + 1;
					folderView->folders = realloc(folderView->folders, MALLOC_EST_FOLDER*sizeof(st_folder)*num_blocks);
					folderView->diff_folders = realloc(folderView->diff_folders, MALLOC_EST_FOLDER*num_blocks);
				}

				folderView->diff_folders[folderView->num_folders] = 0;
				folderView->folders[folderView->num_folders] = exploreFolder(new_dir_uri, ent);
				folderView->num_folders++;

				free(new_dir_uri);
			}
			//Arquivos comuns
			else {
				free(new_dir_uri);
				// Aloca mais memoria caso necessario
				if((folderView->num_files % MALLOC_EST_FOLDER) == 0){
					num_blocks = (folderView->num_files / MALLOC_EST_FILES) + 1;
					folderView->files = realloc(folderView->files, MALLOC_EST_FILES*sizeof(p_ent*)*num_blocks);
					folderView->diff_files = realloc(folderView->diff_files, MALLOC_EST_FILES*num_blocks);
				}

				DebugPrint("adicionando arquivo %s", ent->d_name);

				folderView->diff_files[folderView->num_files] = 0;
				folderView->files[folderView->num_files] = clone_ent(ent);
				folderView->num_files++;
			}
		}
		closedir (dir);

		//Ordena as pastas
		if(folderView->num_folders > 1) {
			qsort(folderView->folders, folderView->num_folders, sizeof(p_st_folder), compareFolder);
		}

		//Ordena os arquivos
		if(folderView->num_files > 1) {
			qsort(folderView->files, folderView->num_files, sizeof(p_ent), compareFiles);
		}

		DebugPrint("Diretorio %s finalizado", dir_uri);
	}
	else {
		/* could not open directory */
		perror ("could not open directory");
	}

	return folderView;
}


// Inicializa uma estrutura do tipo st_folder
p_st_folder initFolderSt(p_ent ent){
	// Variaveis Locais
	p_st_folder folderSt = NULL;

	folderSt = malloc(sizeof(st_folder));
	folderSt->ent = ent;
	folderSt->folders = NULL;
	folderSt->files = NULL;
	folderSt->diff_folders = NULL;
	folderSt->diff_files = NULL;
	folderSt->num_files = 0;
	folderSt->num_folders = 0;

	return folderSt;
}

void printContent(p_st_folder folderSt, int level){
	//Variaveis Locais
	int i, j;

	if(folderSt != NULL){

		//Print folder
		for(i=0; i<folderSt->num_folders; i++){
			for(j=0; j<level; j++){
				printf("|");
			}

			if(folderSt->folders[i]->ent != NULL){
				printf("+%s (folder) %d\n", folderSt->folders[i]->ent->d_name, (int)folderSt->diff_folders[i]);
			}

			printContent(folderSt->folders[i], level+1);
		}

		//Print files
		for(i=0; i<folderSt->num_files; i++){
			for(j=0; j<level; j++){
				printf("|");
			}
			printf("+%s %d\n", folderSt->files[i]->d_name, (int)folderSt->diff_files[i]);
		}
	}
}

void freeContent(p_st_folder folderSt){
	//Variaveis Locais
	int i;

	//DebugPrint("Liberando memoria alocada de %s.", folderSt->ent->d_name);

	free(folderSt->ent);

	for(i=0; i<folderSt->num_folders; i++){
		freeContent(folderSt->folders[i]);
	}
	free(folderSt->diff_folders);
	free(folderSt->folders);

	for(i=0; i<folderSt->num_files; i++){
		free(folderSt->files[i]);
	}
	free(folderSt->diff_files);
	free(folderSt->files);
	free(folderSt);
}

//Clona o ent pois o closedir libera os ents alocados
p_ent clone_ent(p_ent ent){
	p_ent clone = NULL;

	if(ent != NULL){
		clone = malloc(sizeof(struct dirent));

		memcpy(clone, ent, sizeof(struct dirent));
	}

	return clone;
}

//return: 0->sem modif, 1->mod f1, 2->mod f2, 3->mod f1 e f2
int diffFolder(p_st_folder folder1, p_st_folder folder2){
	int f1_count, f2_count;
	int cmp;
	int diff = 0;
	int mask_f1 = 1;
	int mask_f2 = 2;
	int mask_rec;
	p_st_folder aux;

	//DebugPrint("Verificando a diferencas entre %s e %s", folder1->ent->d_name, folder2->ent->d_name);

	//Percorre os arquivos da pasta
	f1_count = 0;
	f2_count = 0;

	while((f1_count != folder1->num_files) && (f2_count != folder2->num_files)){
		cmp = diffFiles(folder1->files[f1_count], folder2->files[f2_count]);
		if(cmp == 0){
			f1_count++;
			f2_count++;
		}
		else if(cmp > 0){
			folder2->diff_files[f2_count] = 1;
			f2_count++;
			diff = diff | mask_f2;
		}
		else {
			folder1->diff_files[f1_count] = 1;
			f1_count++;
			diff = diff | mask_f1;
		}
	}

	while(f1_count != folder1->num_files){
		folder1->diff_files[f1_count] = 1;
		f1_count++;
		diff = diff | mask_f1;
	}

	while(f2_count != folder2->num_files){
		folder2->diff_files[f2_count] = 1;
		f2_count++;
		diff = diff | mask_f2;
	}

	//Percorre as pastas da pasta
	f1_count = 0;
	f2_count = 0;

	while((f1_count != folder1->num_folders) && (f2_count != folder2->num_folders)){
		cmp = diffFiles(folder1->folders[f1_count]->ent, folder2->folders[f2_count]->ent);
		if(cmp == 0){
			mask_rec = diffFolder(folder1->folders[f1_count], folder2->folders[f2_count]);
			if (mask_rec & 1) {
				folder1->diff_folders[f1_count] = 1;
			}
			else if (mask_rec & 2){
				folder2->diff_folders[f2_count] = 1;
			}
			diff = diff | mask_rec;
			f1_count++;
			f2_count++;
		}
		else if(cmp > 0){
			folder2->diff_folders[f2_count] = 1;
			f2_count++;
			diff = diff | mask_f2;
		}
		else {
			folder1->diff_folders[f1_count] = 1;
			f1_count++;
			diff = diff | mask_f1;
		}
	}

	while(f1_count != folder1->num_folders){
		aux = initFolderSt(NULL);
		cmp = diffFolder(folder1->folders[f1_count], aux);
		freeContent(aux);
		folder1->diff_folders[f1_count] = 1;
		f1_count++;
		diff = diff | mask_f1;
	}

	while(f2_count != folder2->num_folders){
		aux = initFolderSt(NULL);
		cmp = diffFolder(aux, folder2->folders[f2_count]);
		freeContent(aux);
		folder2->diff_folders[f2_count] = 1;
		f2_count++;
		diff = diff | mask_f2;
	}

	return diff;

}

int diffFiles(p_ent file1, p_ent file2){
	//precisa comparar tamanho de arquivo tb..., talvez mais coisas
	return strcmp(file1->d_name, file2->d_name);
}

int compareFolder (const void * a, const void * b){
	return strcmp((*((p_st_folder*)a))->ent->d_name, (*((p_st_folder*)b))->ent->d_name);
}

int compareFiles (const void * a, const void * b){
	return strcmp((*((p_ent*)a))->d_name, (*((p_ent*)b))->d_name);
}
